package com.zcyr.core;

import com.zcyr.exception.DatabaseEnvironmentException;
import com.zcyr.exception.TrymeUserSQLException;
import com.zcyr.pojo.DatabaseEnvironmentInfo;
import com.zcyr.pojo.SQLResult;
import com.zcyr.util.SQLTrim;
import com.zcyr.util.StatementTool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public abstract class AbstractDatabaseEnvironment implements IDatabaseEnvironment{

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final String uuid;
    private final String username;
    private final String password;
    private final String url;
    private DatabaseEnvironmentInfo info;
    private Connection connection;
    private int errorCounting = 0;

    protected AbstractDatabaseEnvironment(String url, DatabaseEnvironmentInfo info) {
        this.url = url;
        this.setInfo(info);
        uuid = info.getId();
        username = info.getUsername();
        password = info.getPassword();
    }

    /**
     * 所有该环境的数据库连接执行sql
     *
     * @param sql 要执行的sql
     * @return 执行成功后返回所有结果
     * @throws TrymeUserSQLException        如果SQL语法异常则抛出
     * @throws DatabaseEnvironmentException 如果环境异常则抛出
     */
    @Override
    public List<SQLResult> executeSQL(String sql) throws TrymeUserSQLException, DatabaseEnvironmentException {
        List<SQLResult> results = new ArrayList<>();
        Connection conn;
        try{
            conn = this.getConnection();
            conn.setAutoCommit(false);
        } catch (SQLException e){
            throw new DatabaseEnvironmentException("无法连接到数据库, 环境: " + this + " SQLException: " + e.getMessage(), e);
        }

        sql = SQLTrim.trimSQL(sql, this.getUuid());
        String[] sqlArray = sql.split(";");
        try(Statement stmt = conn.createStatement()){
            Date date = new Date();
            for (String SQL : sqlArray){
                stmt.execute(SQL);
            }
            conn.commit();
            conn.setAutoCommit(false);
            SQLResult result = StatementTool.getResult(stmt);
            result.setTime_consuming(new Date().getTime() - date.getTime());
            results.add(result);
        } catch (SQLException e){
            try {
                conn.setAutoCommit(false);
                conn.rollback();
            } catch (SQLException ex) {
                logger.error("用户环境sql执行异常,回滚事务异常!! env: " + this, ex);
            }
            throw new TrymeUserSQLException(e.getMessage(), e);
        }

        return results;
    }

    /**
     * 检查环境是否过期
     * @return 已过期返回true
     */
    public boolean isExpired(){
        Date date = new Date();
        return (date.getTime() - info.getActive_time().getTime()) > info.getExpire_seconds();
    }

    public void increaseErrorCounting(){
        errorCounting += 1;
    }

    public void resetErrorCounting(){
        errorCounting = 0;
    }

    public int getErrorCounting() {
        return errorCounting;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }

    public String getUuid() {
        return uuid;
    }

    protected void reConnect() throws SQLException {
        if (connection == null || !connection.isValid(20)){
            this.connection = DriverManager.getConnection(url, this.getUsername(), this.getPassword());
        }
    }

    public Connection getConnection() throws SQLException {
        reConnect();
        return connection;
    }

    @Override
    public int releaseTables() throws DatabaseEnvironmentException {
        throw new DatabaseEnvironmentException("TODO: releaseTables()");
    }

    @Override
    public int releaseSchemas() throws DatabaseEnvironmentException {
        throw new DatabaseEnvironmentException("TODO: releaseSchemas()");
    }

    @Override
    public int releaseUsers() throws DatabaseEnvironmentException {
        throw new DatabaseEnvironmentException("TODO: releaseUsers()");
    }

    public DatabaseEnvironmentInfo getInfo() {
        return info;
    }

    public void setInfo(DatabaseEnvironmentInfo info) {
        this.info = info;
    }

    @Override
    public String toString() {
        return "AbstractDatabaseEnvironment{" +
                "uuid='" + uuid + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", url='" + url + '\'' +
                ", info=" + info +
                ", connection=" + connection +
                '}';
    }
}
